/*-------------------------------------------------------------------------
 *
 * nodeGroup.c
 *	  处理分组节点的例程（用于带有 GROUP BY 子句的查询）。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * DESCRIPTION
 *	  分组节点旨在处理带有 GROUP BY 子句的查询。 其外部计划必须提供按分组列（即同组的元组是连续的）指定的顺序排序的元组。 这样，我们只需比较相邻的元组即可找到组边界。
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeGroup.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeGroup.h"
#include "miscadmin.h"
#include "utils/memutils.h"


/*ExecGroup - 返回每组匹配输入元组的一个元组。*/
static TupleTableSlot * fc_ExecGroup(PlanState *fc_pstate)
{
	GroupState *fc_node = castNode(GroupState, fc_pstate);
	ExprContext *fc_econtext;
	TupleTableSlot *fc_firsttupleslot;
	TupleTableSlot *fc_outerslot;

	CHECK_FOR_INTERRUPTS();

	/*
	 * 从节点获取状态信息
	 */
	if (fc_node->grp_done)
		return NULL;
	fc_econtext = fc_node->ss.ps.ps_ExprContext;

	/*ScanTupleSlot保存每组的（复制的）第一个元组。*/
	fc_firsttupleslot = fc_node->ss.ss_ScanTupleSlot;

	/*我们不需要在这里调用ResetExprContext，因为ExecQualAndReset()将每个输入元组重置一次每个元组的内存上下文。*/

	/*如果第一次通过，获取第一个输入元组并确定是否返回它。*/
	if (TupIsNull(fc_firsttupleslot))
	{
		fc_outerslot = ExecProcNode(outerPlanState(fc_node));
		if (TupIsNull(fc_outerslot))
		{
			/*输入为空，因此不返回任何内容*/
			fc_node->grp_done = true;
			return NULL;
		}
		/*将元组复制到firsttupleslot*/
		ExecCopySlot(fc_firsttupleslot, fc_outerslot);

		/*将其设置为qual测试和投影的输入。表达式将把输入元组作为varno OUTER访问。**/
		fc_econtext->ecxt_outertuple = fc_firsttupleslot;

		/*检查条件（HAVING子句）；如果组不匹配，忽略它并进入扫描循环。*/
		if (ExecQual(fc_node->ss.ps.qual, fc_econtext))
		{
			/*
			 * 使用第一个输入元组形成并返回一个投影元组。
			 */
			return ExecProject(fc_node->ss.ps.ps_ProjInfo);
		}
		else
			InstrCountFiltered1(fc_node, 1);
	}

	/*
	 * 这个循环每个输入元组组迭代一次。在循环的开始，我们已经完成了对该组第一个元组的处理，现在需要扫描所有其他组成员。
	 */
	for (;;)
	{
		/*
		 * 扫描所有属于此组的剩余元组
		 */
		for (;;)
		{
			fc_outerslot = ExecProcNode(outerPlanState(fc_node));
			if (TupIsNull(fc_outerslot))
			{
				/* 没有更多组，因此我们完成了 */
				fc_node->grp_done = true;
				return NULL;
			}

			/*
			 * 与第一个元组进行比较，看看这个元组是否属于同一组。如果是，则忽略它并继续扫描。
			 */
			fc_econtext->ecxt_innertuple = fc_firsttupleslot;
			fc_econtext->ecxt_outertuple = fc_outerslot;
			if (!ExecQualAndReset(fc_node->eqfunction, fc_econtext))
				break;
		}

		/*
		 * 我们有下一个输入组的第一个元组。看看我们是否想要返回它。
		 */
		/* 复制元组，设置为qual测试和投影的输入 */
		ExecCopySlot(fc_firsttupleslot, fc_outerslot);
		fc_econtext->ecxt_outertuple = fc_firsttupleslot;

		/*
		 * 检查qual（HAVING子句）；如果组不匹配，则忽略它并循环回去扫描该组的其余部分。
		 */
		if (ExecQual(fc_node->ss.ps.qual, fc_econtext))
		{
			/*
			 * 使用第一个输入元组形成并返回一个投影元组。
			 */
			return ExecProject(fc_node->ss.ps.ps_ProjInfo);
		}
		else
			InstrCountFiltered1(fc_node, 1);
	}
}

/* -----------------
 * ExecInitGroup
 *
 *	为规划器生成的组节点创建运行时信息，并初始化其外部子树
 * -----------------
 */
GroupState *
ExecInitGroup(Group *fc_node, EState *fc_estate, int fc_eflags)
{
	GroupState *fc_grpstate;
	const TupleTableSlotOps *fc_tts_ops;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * 创建状态结构
	 */
	fc_grpstate = makeNode(GroupState);
	fc_grpstate->ss.ps.plan = (Plan *) fc_node;
	fc_grpstate->ss.ps.state = fc_estate;
	fc_grpstate->ss.ps.ExecProcNode = fc_ExecGroup;
	fc_grpstate->grp_done = false;

	/*
	 * 创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_grpstate->ss.ps);

	/*
	 * 初始化子节点
	 */
	outerPlanState(fc_grpstate) = ExecInitNode(outerPlan(fc_node), fc_estate, fc_eflags);

	/*
	 * 初始化扫描槽和类型。
	 */
	fc_tts_ops = ExecGetResultSlotOps(outerPlanState(&fc_grpstate->ss), NULL);
	ExecCreateScanSlotFromOuterPlan(fc_estate, &fc_grpstate->ss, fc_tts_ops);

	/*
	 * 初始化结果槽、类型和投影。
	 */
	ExecInitResultTupleSlotTL(&fc_grpstate->ss.ps, &TTSOpsVirtual);
	ExecAssignProjectionInfo(&fc_grpstate->ss.ps, NULL);

	/*
	 * 初始化子表达式
	 */
	fc_grpstate->ss.ps.qual =
		ExecInitQual(fc_node->plan.qual, (PlanState *) fc_grpstate);

	/*
	 * 预计算内循环的fmgr查找数据
	 */
	fc_grpstate->eqfunction =
		execTuplesMatchPrepare(ExecGetResultType(outerPlanState(fc_grpstate)),
							   fc_node->numCols,
							   fc_node->grpColIdx,
							   fc_node->grpOperators,
							   fc_node->grpCollations,
							   &fc_grpstate->ss.ps);

	return fc_grpstate;
}

/* ------------------------
 *		ExecEndGroup(node)
 *
 * -----------------------
 */
void ExecEndGroup(GroupState *fc_node)
{
	PlanState  *fc_outerPlan;

	ExecFreeExprContext(&fc_node->ss.ps);

	/* 清理元组表 */
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	fc_outerPlan = outerPlanState(fc_node);
	ExecEndNode(fc_outerPlan);
}

void ExecReScanGroup(GroupState *fc_node)
{
	PlanState  *fc_outerPlan = outerPlanState(fc_node);

	fc_node->grp_done = false;
	/* 必须清除第一个元组 */
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	/*
	 * 如果子节点的chgParam不为空，则计划将由第一个ExecProcNode重新扫描。
	 */
	if (fc_outerPlan->chgParam == NULL)
		ExecReScan(fc_outerPlan);
}
